package handler

import (
	"encoding/json"
	"net/http"
	"reflect"
	"strings"

	"gitcode.net/togolife/nfttoken/event"
	"github.com/ethereum/go-ethereum/common"
)

type MintNotify struct {
	Chain        string `json:"chain"`
	ContractAddr string `json:"contractAddr"`
	TxHash       string `json:"txHash"`
	ToAddr       string `json:"toAddr"`
	TokenId      string `json:"tokenId"`
}

type BurnNotify struct {
	Chain        string `json:"chain"`
	ContractAddr string `json:"contractAddr"`
	TxHash       string `json:"txHash"`
	FromAddr     string `json:"fromAddr"`
	TokenId      string `json:"tokenId"`
}

type TransferNotify struct {
	Chain        string `json:"chain"`
	ContractAddr string `json:"contractAddr"`
	TxHash       string `json:"txHash"`
	FromAddr     string `json:"fromAddr"`
	ToAddr       string `json:"toAddr"`
	TokenId      string `json:"tokenId"`
}

type ApprovalNotify struct {
	Chain        string `json:"chain"`
	ContractAddr string `json:"contractAddr"`
	TxHash       string `json:"txHash"`
	TokenOwner   string `json:"tokenOwner"`
	ApprovedAddr string `json:"approvedAddr"`
	TokenId      string `json:"tokenId"`
}

type ApprovalForAllNotify struct {
	Chain        string `json:"chain"`
	ContractAddr string `json:"contractAddr"`
	TxHash       string `json:"txHash"`
	FromAddr     string `json:"fromAddr"`
	ApprovedAddr string `json:"approvedAddr"`
	Flag         bool   `json:"flag"`
}

func NotifyChainLog(log event.LogEmit) {
	var content []byte
	var err error
	postUrl := hdConf.NotifyHttpPath
	switch log.Type {
	case 1:
		if isZeroAddress(log.Transfer.From) {
			v := MintNotify{}
			v.Chain = log.Chain
			v.ContractAddr = log.ContractAddr
			v.TxHash = log.TxHash
			v.ToAddr = strings.ToLower(log.Transfer.To.Hex())
			v.TokenId = log.Transfer.TokenId.String()
			content, err = json.Marshal(v)
			postUrl += "/notify/mint"
		} else if isZeroAddress(log.Transfer.To) {
			v := BurnNotify{}
			v.Chain = log.Chain
			v.ContractAddr = log.ContractAddr
			v.TxHash = log.TxHash
			v.FromAddr = strings.ToLower(log.Transfer.From.Hex())
			v.TokenId = log.Transfer.TokenId.String()
			content, err = json.Marshal(v)
			postUrl += "/notify/burn"
		} else {
			v := TransferNotify{}
			v.Chain = log.Chain
			v.ContractAddr = log.ContractAddr
			v.TxHash = log.TxHash
			v.FromAddr = strings.ToLower(log.Transfer.From.Hex())
			v.ToAddr = strings.ToLower(log.Transfer.To.Hex())
			v.TokenId = log.Transfer.TokenId.String()
			content, err = json.Marshal(v)
			postUrl += "/notify/transfer"
		}
	case 2:
		v := ApprovalNotify{}
		v.Chain = log.Chain
		v.ContractAddr = log.ContractAddr
		v.TxHash = log.TxHash
		v.TokenOwner = strings.ToLower(log.Approval.Owner.Hex())
		v.ApprovedAddr = strings.ToLower(log.Approval.Approved.Hex())
		v.TokenId = log.Approval.TokenId.String()
		content, err = json.Marshal(v)
		postUrl += "/notify/approval"
	case 3:
		v := ApprovalForAllNotify{}
		v.Chain = log.Chain
		v.ContractAddr = log.ContractAddr
		v.TxHash = log.TxHash
		v.FromAddr = strings.ToLower(log.ApprovalForAll.Owner.Hex())
		v.ApprovedAddr = strings.ToLower(log.ApprovalForAll.Operator.Hex())
		v.Flag = log.ApprovalForAll.Approved
		content, err = json.Marshal(v)
		postUrl += "/notify/approvalForAll"
	default:
		hdLog.LogD("Recived notify log, not deal. log is [%v]", log)
		return
	}
	if err != nil {
		hdLog.LogE("Decode log [%v] to json failed! [%v]", log, err)
		return
	}
	// 未配置通知服务器地址时，只打印接收到事件即可
	if len(hdConf.NotifyHttpPath) <= 0 {
		hdLog.LogI("Notify message is [%v]", string(content))
		return
	}
	response, err := http.Post(postUrl, "application/json", strings.NewReader(string(content)))
	if err != nil {
		hdLog.LogE("Post notify failed! postUrl [%v], log[%v] err[%v]", postUrl, log, err)
		return
	}
	// just close body
	response.Body.Close()
}

func isZeroAddress(iaddress interface{}) bool {
	var address common.Address
	switch v := iaddress.(type) {
	case string:
		address = common.HexToAddress(v)
	case common.Address:
		address = v
	default:
		return false
	}

	zeroAddressBytes := common.FromHex("0x0000000000000000000000000000000000000000")
	addressBytes := address.Bytes()
	return reflect.DeepEqual(addressBytes, zeroAddressBytes)
}
